var tools = function(){
			function loadJson(resource) {
				var d = when.defer();
				d3.json(resource, function(error, result) {
					return error ?
						!error.status ?
							d.reject({status: -1, message: "Cannot load resource: " + resource, resource: resource}) :
							d.reject({status: error.status, message: error.statusText, resource: resource}) :
						d.resolve(result);
				});
				return d.promise;
			}
			function createMask(){
				var canvas = d3.select(document.createElement("canvas")).attr('width',windObj.bounds.xMax).attr('height',windObj.bounds.yMax).node();
				//TODO
				var context = canvas.getContext("2d");
				context.fillStyle = "rgba(255, 0, 0, 1)";
				context.fillRect( 0, 0, windObj.bounds.xMax, windObj.bounds.yMax);
				context.fill();
				
				var imageData = context.getImageData(0,0,windObj.bounds.xMax,windObj.bounds.yMax);
				var data = imageData.data;  //[r,g,b,a,r,g,b,a.....]
				return {
					imageData:imageData,
					isVisible:function(x,y){
						var i = parseInt((y * windObj.bounds.xMax + x ) * 4);
						return data[i+3]>0;
					},
					set:function(x,y,rgba){
						var i = parseInt((y * windObj.bounds.xMax + x) * 4);
						data[i] = rgba[0];
						data[i+1] = rgba[1];
						data[i+2] = rgba[2];
						data[i+3] = rgba[3];
						return this;
					}
				}
			}	
			function clearCanvas(canvas) {
				if(!canvas) return;
				canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
				return canvas;
			}
			function newAgent(initial) {
				/**
				 * @returns {Function} a cancel function for a task.
				 */
				function cancelFactory() {
					return function cancel() {
						cancel.requested = true;
						return agent;
					};
				}

				/**
				 * Invokes the specified task.
				 * @param cancel the task's cancel function.
				 * @param taskAndArguments the [task-function-or-value, arg0, arg1, ...] array.
				 */
				function runTask(cancel, taskAndArguments) {

					function run(args) {
						return cancel.requested ? null : _.isFunction(task) ? task.apply(agent, args) : task;
					}

					function accept(result) {
						if (!cancel.requested) {
							value = result;
							agent.trigger("update", result, agent);
						}
					}

					function reject(err) {
						if (!cancel.requested) {  // ANNOYANCE: when cancelled, this task's error is silently suppressed
							agent.trigger("reject", err, agent);
						}
					}

					function fail(err) {
						agent.trigger("fail", err, agent);
					}

					try {
						// When all arguments are resolved, invoke the task then either accept or reject the result.
						var task = taskAndArguments[0];
						when.all(_.rest(taskAndArguments)).then(run).then(accept, reject).done(undefined, fail);
						agent.trigger("submit", agent);
					} catch (err) {
						fail(err);
					}
				}

				var value = initial;
				var runTask_debounced = _.debounce(runTask, 0);  // ignore multiple simultaneous submissions--reduces noise
				var agent = {

					/**
					 * @returns {Object} this agent's current value.
					 */
					value: function() {
						return value;
					},

					/**
					 * Cancels this agent's most recently submitted task.
					 */
					cancel: cancelFactory(),

					/**
					 * Submit a new task and arguments to invoke the task with. The task may return a promise for
					 * asynchronous tasks, and all arguments may be either values or promises. The previously submitted
					 * task, if any, is immediately cancelled.
					 * @returns this agent.
					 */
					submit: function(task, arg0, arg1, and_so_on) {
						// immediately cancel the previous task
						this.cancel();
						// schedule the new task and update the agent with its associated cancel function
						runTask_debounced(this.cancel = cancelFactory(), arguments);
						return this;
					}
				};

				return _.extend(agent, Backbone.Events);
			}			
		  function byte2Hex(n)
		  {
		    var nybHexString = "0123456789ABCDEF";
		    return String(nybHexString.substr((n >> 4) & 0x0F,1)) + nybHexString.substr(n & 0x0F,1);
		  }
		  function RGB2Color(r,g,b)
		  {
		    return '#' + byte2Hex(r) + byte2Hex(g) + byte2Hex(b);
		  }
			function makeColorGradient(frequency1, frequency2, frequency3,
			 phase1, phase2, phase3, center, width, len)
		  {
		    if (center == undefined)   center = 128;
		    if (width == undefined)    width = 127;
		    if (len == undefined)      len = 50;
		
		    for (var i = 0; i < len; ++i)
		    {
		    	var red = Math.sin(frequency1*i + phase1) * width + center;
		    	var grn = Math.sin(frequency2*i + phase2) * width + center;
		    	var blu = Math.sin(frequency3*i + phase3) * width + center;
		    	document.write( '<font color="' + RGB2Color(red,grn,blu) + '">&#9608;</font>');
		    }
		  }
			return {
					loadJson:loadJson,
					createMask:createMask,
					clearCanvas:clearCanvas,
					newAgent:newAgent,
					makeColorGradient:makeColorGradient
			}
}();